// client_length.go
//
// 功能：客户端按长度前缀协议封包发送，并能正确解包服务器回显。
// 核心：pack/unpack 函数封装长度字段处理，避免粘包。
package main

import (
	"encoding/binary"
	"io"
	"log"
	"net"
	"time"
)

const lengthAddr = "127.0.0.1:7100"

func main() {
	conn, err := net.Dial("tcp", lengthAddr)
	if err != nil {
		log.Fatalf("dial error: %v", err)
	}
	defer conn.Close()

	messages := [][]byte{
		[]byte("login:user=alice"),
		[]byte("pub:topic=order body=buy"),
		[]byte("ping"),
	}

	for _, msg := range messages {
		packed := pack(msg)
		if _, err := conn.Write(packed); err != nil {
			log.Fatalf("[LEN] write error: %v", err)
		}
		log.Printf("[LEN] 已发送 len=%d payload=%q", len(msg), msg)
	}

	// 按协议解包服务器回显
	for range messages {
		payload, err := unpack(conn)
		if err != nil {
			log.Fatalf("[LEN] unpack error: %v", err)
		}
		log.Printf("[LEN] 收到回显：%q", payload)
	}
	time.Sleep(200 * time.Millisecond)
}

// pack 为 payload 加上 4 字节大端长度头。
func pack(payload []byte) []byte {
	buf := make([]byte, 4+len(payload))
	binary.BigEndian.PutUint32(buf[:4], uint32(len(payload)))
	copy(buf[4:], payload)
	return buf
}

// unpack 从连接中读取一帧完整消息。
func unpack(conn net.Conn) ([]byte, error) {
	lenBuf := make([]byte, 4)
	if _, err := io.ReadFull(conn, lenBuf); err != nil {
		return nil, err
	}
	payloadLen := binary.BigEndian.Uint32(lenBuf)
	payload := make([]byte, payloadLen)
	if _, err := io.ReadFull(conn, payload); err != nil {
		return nil, err
	}
	return payload, nil
}
